/*
 * This file or a portion of this file is licensed under the terms of
 * the Globus Toolkit Public License, found in file GTPL, or at
 * http://www.globus.org/toolkit/download/license.html. This notice must
 * appear in redistributions of this file, with or without modification.
 *
 * Redistributions of this Software, with or without modification, must
 * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
 * some other similar material which is provided with the Software (if
 * any).
 *
 * Copyright 1999-2004 University of Chicago and The University of
 * Southern California. All rights reserved.
 */
package org.griphyn.vdl.planner;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.io.*;

/**
 * Class which implements a topological sort of a graph.
 *
 * @author Jens-S. Vöckler
 * @author Yong Zhao
 *
 * @version $Revision: 50 $
 * @see Graph
 */
public class Topology 
{
  /**
   * The graph representation.
   */
  private Graph m_graph;

  /**
   * Hashtable to map vertex name to array index.
   */
  private Map m_vertexMap;

  /**
   * Array to keep the name of each vertex.
   */
  private String[] m_vertices;

  /**
   * Array to keep the in-degree of each vertex.
   */
  private int[] m_inDeg;

  /**
   * Queue to keep 0 in-degree vertices.
   */
  private List m_inQueue;

  /**
   * Constructor, given a graph, construct internal objects
   * needed for topological sort.
   *
   * @param graph is a graph representation.
   */
  public Topology( Graph graph ) 
  { 
    m_graph = (Graph) graph.clone();
    init();
  }

  /**
   * Initializes in-degree array and the corresponding 0 in-degree
   * queue.
   */
  public void init() 
  {
    // allocate memory for collections
    int n = m_graph.order();
    m_vertexMap = new HashMap();
    m_vertices = new String[n];
    m_inDeg = new int[n];
    m_inQueue = new ArrayList();

    // map nodes
    int i = 0;
    for ( Iterator e=m_graph.getVertices(); e.hasNext(); ) {
      String v = (String) e.next();
      m_vertices[i] = new String(v);
      m_vertexMap.put( new String(v), new Integer(i) );
      m_inDeg[i] = m_graph.inDegree(v);
      ++i;
    }

    // put 0 in-degree vertices in the queue
    for ( i=0; i<n; i++ ) { 
      if ( m_inDeg[i] == 0 ) m_inQueue.add( new Integer(i) );
    }
  }

  /**
   * Sorts the graph topologically. It first invokes the {@link #init()} 
   * method to start the graph over, and initialize data structures. 
   *
   * @return A sorted list of vortex names. 
   */
  public String[] sort() 
  {
    int n = m_graph.order();
    String[] s = new String[n];

    int cnt = 0;
    int i;
    while ( m_inQueue.size() > 0 ) { 
      i = ((Integer)m_inQueue.remove(0)).intValue();
      s[cnt++] = new String(m_vertices[i]); 

      List neighbors = m_graph.neighbors( m_vertices[i] ); 
      for ( int j=0; j<neighbors.size(); j++ ) { 
	String w = (String) neighbors.get(j);
	int k = ((Integer) m_vertexMap.get(w)).intValue();
	m_inDeg[k] -= 1;
	if ( m_inDeg[k] == 0 ) m_inQueue.add( new Integer(k) );
      } 
    } 
    if ( cnt != n )
      throw new Error("The graph is not acyclic");

    return s; 
  }

  /**
   * Sorts the graph topologically, but also with regards to stages. It
   * first invokes the {@link #init()} method to start the graph over,
   * and initialize data structures.
   *
   * @return a sorted list of zero in-degree vertices.
   */
  public String[] stageSort() 
  {
    int n = m_inQueue.size();
    if ( n == 0 ) return null;
  
    String[] s = new String[n];
    int cnt = 0;
    while ( cnt < n ) {
      int i = ((Integer)m_inQueue.remove(0)).intValue();
      s[cnt++] = new String(m_vertices[i]); 

      List neighbors = m_graph.neighbors( m_vertices[i] ); 
      for ( int j=0; j<neighbors.size(); j++ ) { 
	String w = (String) neighbors.get(j);
	int k = ((Integer) m_vertexMap.get(w)).intValue();
	m_inDeg[k] -= 1;
	if (m_inDeg[k] == 0) m_inQueue.add( new Integer(k) );
      } 
    }
    return s;
  }

  /**
   * The main method contains some simple tests for the sorting algorithms.
   * The graph to test with is read from stdin. 
   *
   * @param args are commandline arguments. 
   */
  public static void main(String[] args)
  {
    Graph g = new Graph(new BufferedReader(new InputStreamReader(System.in)));
    Topology t = new Topology(g);
    String[] r = t.sort();

    System.out.println( "The topological sorting result:" );
    for ( int i=0; i < r.length; i++ ) System.out.print(r[i] + " ");
    System.out.println();

    System.out.println("The stage-by-stage sorting result:");
    t.init();
    while ( (r=t.stageSort()) != null ) {
      for ( int i=0; i < r.length; i++ ) System.out.print( r[i] + " " );
      System.out.println();
    }

    System.out.println( "Let's do it in reverse" );
    t = new Topology(g.reverseGraph());
    r = t.sort();
    System.out.println( "The topological sorting result:" );
    for ( int i=0; i < r.length; i++ ) System.out.print( r[i] + " " );
    System.out.println();
  }
} 

